import re
import os
from ruamel import yaml
from ruamel.yaml.emitter import Emitter
from m4_macro_parser import M4MacroParser

Emitter.MAX_SIMPLE_KEY_LENGTH = 4096


class AutoconfMacroParser(M4MacroParser):
    AUTOCONF_MACRO_PARAMS = {
        "AC_ARG_ENABLE": ["FEATURE", "HELP-STRING", "ACTION-G", "ACTION-D"],
        "AC_ARG_WITH": ["FEATURE", "HELP-STRING", "ACTION-G", "ACTION-D"],
        "AC_CHECK_FILE": ["FILE", "ACTION-F", "ACTION-N"],
        "AC_CHECK_FILES": ["FILES", "ACTION-F", "ACTION-N"],
        "AC_CHECK_HEADER": ["HEADER-FILE", "ACTION-F", "ACTION-N", "INCLUDES"],
        "AC_CHECK_HEADERS": ["HEADER-FILES", "ACTION-F", "ACTION-N", "INCLUDES"],
        "AC_CHECK_HEADERS_ONCE": ["HEADER-FILES"],
        "AC_CHECK_LIB": ["LIBRARY", "FUNCTION", "ACTION-F", "ACTION-N", "OTHER-LIBRARIES"],
        "AC_SEARCH_LIBS": ["FUNCTION", "SEARCH-LIBS", "ACTION-F", "ACTION-N", "OTHER-LIBRARIES"],
        "PKG_CHECK_MODULES": ["PREFIX", "MODULES", "ACTION-F", "ACTION-N"],
        "AC_CHECK_PROGS": ["VARIABLE", "PROGS", "VALUE-N", "PATH"],
        "AC_CHECK_PROG": ["VARIABLE", "PROG", "VALUE-F", "VALUE-N", "PATH", "REJECT"],
        "AC_CHECK_TARGET_TOOLS": ["VARIABLE", "PROGS", "VALUE-N", "PATH"],
        "AC_CHECK_TARGET_TOOL": ["VARIABLE", "PROG", "VALUE-N", "PATH"],
        "AC_CHECK_TOOLS": ["VARIABLE", "PROGS", "VALUE-N", "PATH"],
        "AC_CHECK_TOOL": ["VARIABLE", "PROG", "VALUE-N", "PATH"],
        "AC_PATH_PROGS_FEATURE_CHECK": ["VARIABLE", "PROGS", "FEATURE-TEST", "ACTION-N", "PATH"],
        "AC_PATH_PROGS": ["VARIABLE", "PROGS", "VALUE-N", "PATH"],
        "AC_PATH_PROG": ["VARIABLE", "PROG", "VALUE-N", "PATH"],
        "AC_PATH_TARGET_TOOL": ["VARIABLE", "PROG", "VALUE-N", "PATH"],
        "AC_PATH_TOOL": ["VARIABLE", "PROG", "VALUE-N", "PATH"],
        "PKG_CHECK_EXISTS": ["MODULES", "ACTION-F", "ACTION-N"]
    }

    def __init__(self):
        super().__init__()
        self.configure_ac_content = None
        self.configure_ac_dir = None
        self.configure_ac_path = None
        self.m4_macros = None
        self.macros = {}

    def load_configure_ac(self, configure_ac_path):
        self.configure_ac_path = configure_ac_path
        self.configure_ac_dir = os.path.dirname(configure_ac_path)
        with open(configure_ac_path, 'r', encoding='utf-8', errors='ignore') as file:
            self.configure_ac_content = file.read()

    def parse_macros(self):
        self.m4_macros = self.parse_m4_macros(self.configure_ac_content) if not self.m4_macros else self.m4_macros
        for m in self.m4_macros:
            # print(m['macro_name'], m['macro_params'])
            if len(m['macro_params'])==1 and m['macro_params'][0]=='$@':
                continue
            self.add_macro(m['macro_name'], m['macro_params'])
        return self.macros

    def add_macro(self, macro_name, macro_args):
        if len(macro_args) == 0:
            key = f"{macro_name}."
        else:
            macro_args[0] = re.sub(r'\s+', ' ', macro_args[0])
            macro_args[0] = macro_args[0].strip()
            key = f"{macro_name}.{macro_args[0]}"
        if macro_name not in self.AUTOCONF_MACRO_PARAMS:
            return

        if len(macro_args) > len(self.AUTOCONF_MACRO_PARAMS[macro_name]):
            print(f"WARNING: {macro_name} has too many params: {macro_args}")

        while key in self.macros:
            key += '~'

        self.macros[key] = {}
        for arg, index in zip(macro_args, range(len(macro_args))):
            if not arg:
                continue
            key2 = self.AUTOCONF_MACRO_PARAMS[macro_name][index]
            self.macros[key][key2] = arg.strip()

        if macro_name == 'PKG_CHECK_MODULES':
            self.fixup_pkg_modules(self.macros[key])

    def fixup_pkg_modules(self, macro):
        modules = re.split(r'(?<![>=!<])\s+(?![>=!<])', macro['MODULES'])
        modules = list(filter(None, modules))
        macro['MODULES'] = modules

    def set_block_style(self, data):
        for key, value in data.items():
            if isinstance(value, dict):
                self.set_block_style(value)
            elif '\n' in str(value):
                data[key] = yaml.scalarstring.LiteralScalarString(value)

    def save_macros(self, dir=None):
        # print(self.macros)
        if dir is None:
            dir = self.configure_ac_dir
        self.set_block_style(self.macros)
        fn = os.path.join(dir, "macros.yaml")
        with open(fn, 'w', encoding='utf-8') as file:
            yml = yaml.YAML()
            yml.dump(self.macros, file)
        return self.macros

# if __name__=='__main__':
#     # Usage
#     parser = AutoconfMacroParser()
#     parser.load_configure_ac('code/autoconf_examples/glibc/configure.ac')
#     result = parser.parse_macros()
#     parser.save_macros()
